package org.NooLab.itexx;

import java.util.ArrayList;

import org.NooLab.utilities.logging.PrintLog;
import org.NooLab.utilities.objects.StringedObjects;

import org.NooLab.structures.ITexxPublicIntf;

import org.NooLab.tikaserver.intf.TikaSrvCmdPkgIntf;
import org.NooLab.tikaserver.tcp.agents.TikaSrvCmdPkg;

import org.NooLab.itexx.co.CoWorker;
import org.NooLab.itexx.co.CoWorkers;
import org.NooLab.itexx.co.ExtWorkers;
import org.NooLab.itexx.co.targets.CoAstor;
import org.NooLab.itexx.co.targets.CoDocumentObserver;
import org.NooLab.itexx.co.targets.CoListenerServer;
import org.NooLab.itexx.co.targets.CoSomClients;
import org.NooLab.itexx.co.targets.CoTexx;
import org.NooLab.itexx.co.targets.ExtTikaServer;
import org.NooLab.itexx.tcp.ClientMsgHandlerIntf;
import org.NooLab.itexx.tcp.CommBox;



/**
 * TODO: 
 * 
 *       launch4j -> windows
 * 
 *       On Mac OS X there is a program called Jar Bundler that is installed when 
 *       you install the free (assuming that you already own a copy of Mac OS X) 
 *       Xcode Developer Tools that allows you to bundle a JAR file inside a native 
 *       Mac OS X "*.app" application bundle with a nice and shiny icon just like other apps.
 *        
 *       Bundles on MAc
 *       http://developer.apple.com/library/mac/#documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html
 *        
 *       System.getenv() has an entry that describes the command line !!
 *        
 *       -------------------------------------------------------
 *        
 *       Texx requires the tikaserver to run!!!
 *       thus, iTexx should check whether it knows about it!!
 *       
 *       Else...
 *       
 *       - instead of using command line arguments, a simple ini config file using the same format
 *         should be possible as well... the cfg file is in the dir [jar file folder]/cfg/
 *         and uses the same name as the jar, i.e. e.g. NooLabTexx.clia
 *       
 *       - iTexx should receive and understand messages (console or TCP) from clients 
 *          that contain  "@iTexx ([processname]):" 
 *       - all applications should know about their processnames, which comes via CLI
 *            
 *       - all applications should understand a CLI argument "-waitfor: [GUID secret]", 
 *         which is issued and maintained by iTexx ("this") 
 *         
 *       - correspondingly iTexx ("this") should receive process messages (heading = "@iTexx"),
 *         which contain a confirmation that the application runs successfully 
 *         the message looks like simple xml "@iTexx :: <process name="[processname]" secret="[guid]" \><confirmation operation="1"\>
 *               ... the message is completed to full xml and then interpreted
 *               
 *       - IMPORTANT all applications should understand stop files!!
 *                   these are issued by iTexx upon its start and stop,
 *                   written into  
 * 
 * 
 * 
 * Description
 * 
 * This application starts the modules of "iTexx" as separate processes. 
 * These processes are started as classes into a freshly spawned JVM, yet,
 * despite they are not executable, they need a particular set of parameters
 * to start properly.
 * This startup is accomplished within separate threads from which it invokes the
 * launch-method of the JavaProcessBuilder;
 * 
 * One exception is so-called TikaServer, which is run as an executable jar
 * by ITexx (with capital "I" == "this"). The reason are severe problems with Tika, 
 * which is unable to  select the right classloader to find its own resources, 
 * if it is started through the JavaProcessBuilder, i.e. within the namespace 
 * of a master-classloader. 
 *  
 * ITexx (this class) also runs a thread that is supervising the processes.
 * In order to avoid runaway memory consumption, the basic collecting processes 
 * are periodically restarted.
 * 
 * Any module that is maintained by ITexx can request its own restart, e.g. if
 * a certain number of documents have been processed, or if the application is 
 * in idle state. 
 * 
 * All this is possible only because the whole "iTexx" - framework is data driven;
 * all data are stored in a database, for all major states of their processing.
 * 
 * That is, the document with its id is one the main "business object" for the
 * assimilation of new documents. 
 * 
 * For processing by the SOM there are several further business objects
 *  - document and associated contexts 
 *  - the SOMs (there could be many, at least there are 2)
 *  - other major objects, like the Markov-object, which runs also in its own 
 *    process 
 *  
 * All business objects can persist, and thus they are all identified in a global manner. 
 *
 * Neither the major processes nor the business objects do NOT communicate directly 
 * with each other.
 * Any of them works just on the data view which it understands.  
 * 
 * ----------------
 * 
 * The "data orientation" has further benefits.
 * For instance, modules can be updated independently, they can be run on different machines,
 * or at different periods of time, they can be restarted without stopping the whole framework;
 * Most important, there could be many instances of modules of the same kind, where the 
 * number of the instances may vary from type to type. 
 * 
 * 
 * ----------------
 * 
 * The project "NooLabSomClients" contains three groups of functions:
 *   - the clients or the interfaces to them (a further SOM [flavors: idea, astor, purple], Markov, Spela, etc.);
 *     the main portion of code may be "outsourced" to other packages;
 *   - the means to query a SOM or a collection of SOM in order to retrieve the 
 *     input data for the client, e.g. the histogram view for building a websom 
 *   - the administration for the client 
 
the results produced  * 
 * ------------------------------------------------
 * 
 * Weka sources
 * https://svn.scms.waikato.ac.nz/svn/weka/trunk/weka/src/main/java/weka/core/
 * 
 * starting processes and supervising it
  
  		Process p = Runtime.getRuntime().exec( jarStartCmd );
        p.waitFor();

        try {
			
        	p.waitFor();
			
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
 * @author kwa
 *
 */
public class ITexx 
						implements 
									ClientMsgHandlerIntf,
									ITexxPublicIntf{

	boolean appIsRunning=false;
	
	CoWorkers  coWorkers;
	ExtWorkers extWorkers;
	
	CommBox[]  commBoxes ;
	ITexxFactory itxFactory ;
	ITexx itexx;
	
	public PrintLog out = new PrintLog(2,true);
	
	// ======================================================================== 
	public ITexx(ITexxFactory factory) throws Exception {
		
		itxFactory = factory;
		
		coWorkers = new CoWorkers(itxFactory);
		extWorkers = new ExtWorkers(itxFactory);
		
		itexx = this;
	}
	/**
	 * called by the factory
	 */
	public void createProcesses() {
		
		
		// 0. check for H2 ...
		 
		int numberOfDefinedProcesses = 0;
		
		try{
			
			numberOfDefinedProcesses = extWorkers.size() + coWorkers.size();
			
			out.print(2, "# Processes defined so far : "+numberOfDefinedProcesses);
			prepareCommServers();
			
			int tikaServerPort = 7250;
			// 0. external Tika-Server : note that this is a WORK AROUND due to 
			//    some problems of Tika when getting started via ProcessBuilder
			ExtTikaServer tikasrvExt = new ExtTikaServer(itxFactory,this);
			tikasrvExt.configure( tikaServerPort,0 );
			
			// 1. SomClients process : only the frame is being started, not the clients themselves
			//    instances of base clients are started later, in the 5th step, or so
			//    the "SomClients" instance is the central "breeding station" for any process that
			//    takes data from the first layer
			// CoSomClients somClientsCo = new CoSomClients(itxFactory,this);
			// somClientsCo.configure() ; // adds the process definition to coWorkers list
			           /*
			            * note that we have to start it here, not in Astor, since the SomClients
			            * hosts different kinds of clients or keeps the references of registered ones.
			            * 
			            * Any query to any of the SOM is offered by SomQuery (another client)
			            */
		 	CoDocumentObserver docObserverCo = new CoDocumentObserver(itxFactory,this);
			docObserverCo.configure(0,0) ; // adds the process definition to coWorkers list
			
            
			// 2. Listeners and DocumentObserver
		 	CoListenerServer listenerServerCo = new CoListenerServer(itxFactory,this);
		 	// listenerServerCo.configure() ; // adds the process definition to coWorkers list

			// 3. 
			CoTexx texxler = new CoTexx(itxFactory,this);
			tikaServerPort = tikasrvExt.getProcessDef().getProposedPort() ; // the effective port used to start the tikaserver
			texxler.configure(tikaServerPort,0) ; // adds the process definition to coWorkers list
			
			
			// 4. the basic SOM layer
			CoAstor astorSom = new CoAstor(itxFactory,this); 
			// astorSom.configure(0);
			
			// 5. the secondary SOM layers in SomClients, only basic instances (others are started adaptively) 
			//   a. the general SOM (untargeted)
			//   b. the host managing the targeted SOMs
			CoSomClients somClientsCo = new CoSomClients(itxFactory,this);
			// somClientsCo.configure(0,5) ; // adds the process definition to coWorkers list
			

			
			// 5. the application linkage, which currently just implements a listener to the clipboard
			//    (later, it connects to app specific listeners, which work as plugins, e.g. firefox, or word, or the screen reader)
			// 
			
			
			// 
			
			// x.
			
			int numberOfFreshlyDefinedProcesses = extWorkers.size() + coWorkers.size()- numberOfDefinedProcesses;
			
			out.print(2, "# Processes defined now : "+(extWorkers.size() + coWorkers.size()));
			out.print(2, "# Processes to be launched : "+numberOfFreshlyDefinedProcesses);

			// ............................................
			
			// extWorkers.launch(100, false) ;
			
			coWorkers.launch(2000, true);

			// ............................................
			
		}catch(Exception e){
			e.printStackTrace() ;
		}
			}
	
	public void prepareCommServers() throws Exception{
		
		ArrayList<Integer> ports = itxFactory.serverPorts;
		
		CommBox  commBox ;
		commBoxes = new CommBox[ports.size()] ;
		
		for (int i=0;i<ports.size();i++){
			
			// is there any active CommBox that is already using the port
			
			commBox = new CommBox( itexx, "iTexx-"+ (ports.get(i)), ports.get(i) , -1) ;
			
			commBoxes[i] = commBox; 
		}
	}
	public CoWorkers getCoWorkersContainer() {
	
		return coWorkers;
	}
	
	
	public ExtWorkers getExtWorkersContainer() {
		 
		return extWorkers;
	}
	public void closeExternalServers(){
	
		StringedObjects sob = new StringedObjects();
		TikaSrvCmdPkgIntf cmd = new TikaSrvCmdPkg();
		cmd.setStop();
	
		String encstr = sob.encode(cmd);
	
		out.print(2, "before sending stop...");
		
		try {
			
			// we need to do this for all processes
			coWorkers.stop();
			
			extWorkers.stop();
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		
	}
	
	
	// ========================================================================
	
	
	public void start() {
		
		(new AppLoop()).start() ;
	}
	class AppLoop implements Runnable{
		
		Thread appLoop;
		
		public AppLoop(){
		
			appLoop = new Thread(this,"itxAppLoop") ;
		}

		public void start(){
			appLoop.start();
		}
		
		
		@Override
		public void run() {
			 
			appIsRunning = true;
			
			while (appIsRunning){
				
				
				
				out.delay(10);
			}// ->
			
		}
		 
	}


	public void close() {
		
		appIsRunning = false;
		
		try {

			coWorkers.stop();
			
			extWorkers.stop();
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		PrintLog.Delay(500);
		closeExternalServers();
	}

 
	
}

